#! /bin/bash

SCRIPTNAME="${0##*/}"
USAGE="usage: ${SCRIPTNAME} [-v (verbose)] [-h] [-n <total>] [-p <par max>] cmd ..."

total=0
maxpar=1
verbose=false

while getopts :vhn:p: OPT
do
    case $OPT in
	v)
	    verbose=true
	    ;;
	h)
	    echo "${USAGE}"
	    exit
	    ;;
	n)
	    total="$OPTARG"
	    ;;
	p)
	    maxpar="$OPTARG"
	    ;;
	*)
	    echo "${USAGE}" >&2
	    exit 2
    esac
done
shift $[ OPTIND - 1 ]

cmd=("$@")

if [ -z "$*" ]; then
    echo "${USAGE}" >&2
    exit 2
fi

function message() {
    if $verbose; then
	function message() {
	    echo "$*"
	}
    else
	function message() {
	    :
	}
    fi
    message "$*"
}

set -m
forked_jobs=0

function runjob() {
    if [ $forked_jobs -lt $total ]; then
	"${cmd[@]}" &
	((forked_jobs++))
 	message "${FUNCNAME}(): $forked_jobs"
    else
	trap - SIGCHLD
	message "${FUNCNAME}(): exit"
    fi
}

trap "runjob" SIGCHLD

# start jobs until $maxpar
# then start a new job for every one that exits until $total

for((i=1; i<=maxpar; i++)); do
    "${cmd[@]}" &
    ((forked_jobs++))
    message "main(): $forked_jobs now forked"
done

for((i=1; i<=total; i++)); do
    wait
done

